iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
Software Development

Go Clean Architecture API 開發全攻略系列 第 2

架構選擇:我們為何在 Go 專案中採用六角形架構

  • 分享至 

  • xImage
  •  

在上一篇文章中,我們建立了專案的基礎目錄結構。你可能會想,為什麼要分得這麼細?不能直接在 main.go 裡處理所有事情嗎?

問得好。答案在於我們的目標:打造一個可維護、可測試、可擴展的「API」應用。為了達到這個目標,我們需要一個優良的軟體架構來指導我們,而我們選擇的就是六角形架構(Hexagonal Architecture)

什麼是六角形架構?

六角形架構,又稱為Ports and Adapters,或廣義上的Clean Architecture,是由 Alistair Cockburn 提出的一種軟體架構模式。

它的核心思想非常簡單:

保護你的核心業務邏輯,使其與外部世界(例如 UI、資料庫、第三方服務)完全解耦。

想像你的應用程式是一個城堡。城堡最核心、最寶貴的是國王和他的寶藏(也就是你的業務邏輯)。城堡需要與外界溝通(例如接收糧食、派遣軍隊),但你不想讓任何人隨便就闖進來。於是,你在城堡上開了幾個固定的城門(Ports),並規定了所有進出的規則。任何想跟城堡打交道的人,都必須透過這些城門,並使用你指定的交通工具(Adapters)。

https://ithelp.ithome.com.tw/upload/images/20250901/20128815foj9FWvW08.png

對應到我們的程式碼中:

  • 內部核心 (Inside / Domain):應用程式的核心業務邏輯。它包含了 Usecase(業務流程)和 Domain(業務實體)。這部分程式碼是純粹的,它不應該知道任何外部技術的細節。它不知道資料是存在 MySQL 還是 MongoDB,也不知道請求是來自 HTTP API 還是命令列。

  • Ports:是內部核心與外部世界溝通的介面(Interface)。它定義了「需要做什麼」,但不關心「如何做」。例如,一個 Repository 埠可能定義了 CreateUserGetUserByID 這兩個方法,但它不關心具體是用 GORM 還是原生 SQL 來實現。

  • Adapters:是 Ports 的具體實現,是連接外部技術和我們核心業務的「橋樑」。

    • Primary/Driving Adapters:驅動我們的應用程式執行。例如,Gin 的 HTTP Handler 就是,它接收 HTTP 請求,然後呼叫核心的 Usecase
    • Secondary/Driven Adapters:被我們的應用程式所驅動。例如,使用 GORM 實現 Repository Ports 的程式碼就是。核心業務邏輯透過 Ports 呼叫它,它負責與 MySQL 資料庫進行溝通。

最重要的原則:依賴關係倒轉

六角形架構能成功的關鍵在於依賴關係倒轉原則(Dependency Inversion Principle)

所有依賴關係都必須指向內部核心。

這意味著:

  • Adapters(外部)可以依賴核心(內部)。
  • 核心(內部)絕對不能依賴 Adapters(外部)。

這就是為什麼我們需要「Ports」(Interface)。Usecase 依賴的是 Repository 這個Interface(屬於核心的一部分),而不是 GORM 的具體實作(屬於 Adapters)。這樣,核心就保持了它的純粹性。

採用六角形架構的好處

  1. 極高的可測試性:我們可以輕易地為核心業務邏輯撰寫單元測試,因為它不依賴任何外部服務。我們只需要建立一個「假的」Adapter(Mock Adapter)來模擬資料庫行為,就可以獨立測試業務流程的正確性。

  2. 技術無關性與靈活性:我們的核心業務邏輯與外部技術解耦。這意味著,如果我們未來想把 Web 框架從 Gin 換成 Echo,或者把資料庫從 MySQL 換成 PostgreSQL,我們需要做的僅僅是更換或新增一個 Adapter,而核心業務邏輯完全不需要改動。

  3. 清晰的職責分離:架構強迫我們思考每一段程式碼的職責。Controller 只負責處理 HTTP 請求與回應,Usecase 只負責業務流程,Repository 只負責資料庫存取。這讓程式碼更容易理解和維護。

我們的目錄結構如何對應?

現在,再看我們設計的目錄結構,就清晰多了:

  • internal/domain: 核心中的核心。定義業務實體和規則。
  • internal/usecase: 核心業務邏輯。它會定義 Port(Interface),並依賴 domain
  • internal/controller: Primary Adapter。處理 HTTP 請求,依賴 usecase
  • internal/database/mysql: Secondary Adapter。實現 usecase 中定義的資料庫 Port,與資料庫互動。

依賴方向永遠是:controller -> usecase <- database/mysql,而 usecasedatabase/mysql 都指向 domain

總結

六角形架構初看起來可能有些複雜,甚至對於小型專案來說有點「殺雞用牛刀」。但它所帶來的長期效益是巨大的。它強迫我們寫出低耦合、高內聚、易於測試和維護的程式碼。

養成這種架構思維,是從「寫能跑的程式」到「寫能活得久的產品」的關鍵一步。


上一篇
Go 專案實戰:從零到一打造可維護的 API 服務
下一篇
設定管理哲學:使用 Viper 與環境變數打造靈活的 Config
系列文
Go Clean Architecture API 開發全攻略3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言